// XCoreLoader.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "console.h"
#include "multiboot.h"

#include "GDT.h"

bool IsX64Suitable();

static union MULTIBOOT2
{
	MULTIBOOT2_HEADER header;
	
	BYTE buff[1024];
} __multiboot = 
{
	MULTIBOOT2_HEADER_MAGIC,
	MULTIBOOT2_HEADER_ARCH,
	MULTIBOOT2_HEADER_LEN,
	MULTIBOOT2_HEADER_GETCHECKSUM(MULTIBOOT2_HEADER_LEN)
};


static void __declspec(naked) isr_empty()
{
	_asm pushad;

    _asm
	{		
        popad									
        iretd									
    }											
};

void XCoreLoaderProc();

#pragma pack(push, 1)
typedef struct  
{
	QWORD Base; // base address QWORD
	QWORD Length; // length QWORD

	WORD Type; // entry Ttpe
	WORD ACPI; // exteded
} SMAP_ENTRY, *PSMAP_ENTRY;

typedef struct
{
	DWORD		magic;					// 0x16081986
	WORD		mmEntriesCount;
	PSMAP_ENTRY mmTable;  

	BYTE		reserved;
	BYTE		checksum;				// checksum + summ (all bytes w/o checksum) = 0
} XPRELDRINFO, *PXPRELDRINFO;
#pragma pack(pop, 1)

enum LOADER
{
	LDR_XPRELDR   = 1,
	LDR_MULTIBOOT = 2
};

void __declspec(naked) XLoader()
{
	
	__asm
	{
		cmp eax, 0x04111985
		jne checkMultiboot

		mov edx, LDR_XPRELDR

		jmp initialize
	
	checkMultiboot:
		cmp eax, 0x2BADB002;
		je	multiboot;

		xor ebx, ebx

	multiboot:
		; ebx - address of multiboot header
		mov edx, LDR_MULTIBOOT

initialize:
		mov ax, ds
		mov ss, ax

		mov esp, 1024 * 1024 * 2
		mov ebp, esp

		jmp XCoreLoaderProc
	}
	// state:
	// edx	- loader type:
	//	1	- xpreldr.sys
	//  2	- multiboot
	// xpreldr.sys:
	//	ebx	- addr of XPRELDRINFO table
	// multiboot:
	//  ebx - addr of multiboot header
}


void __declspec(naked) BootHeaderError(register char* c)
{
	putline("Boot header is corrupted:");
	putline(c);
	halt();
}

void ProcessXPreLdrData(PXPRELDRINFO pLdrInfoTable)
{
	if (pLdrInfoTable->magic != 0x16081986)
	{
		// invalidHeader
		BootHeaderError("Invalid magic number");
	}

	LPBYTE pBuff = (LPBYTE)pLdrInfoTable;
	BYTE cs = 0;
	for (int i = 0; i < sizeof(XPRELDRINFO); i++)
	{
		cs += *pBuff++;
	}

	if (cs)
	{
		// invalid checksum
		BootHeaderError("Invalid checksum");
	}

	if (pLdrInfoTable->mmEntriesCount == 0 || pLdrInfoTable->mmTable == NULL)
	{
		// memory map is empty or doesn't exist
		BootHeaderError("No memory map was found");
	}

	// Sorting entries in memory map by BaseAddress using modified bubble algorythm
	// We must keep in mind that this table usually consist of few elements (less then 20)
	// so this algorythm is fast enough for this purpose
	if (pLdrInfoTable->mmEntriesCount > 1)
	{
		bool needToRecheck = true;
		for (int j = 0; needToRecheck && j < pLdrInfoTable->mmEntriesCount; j++)
		{
			needToRecheck = false;

			for (int i = 1; i < pLdrInfoTable->mmEntriesCount; i++)
			{
				if (pLdrInfoTable->mmTable[i - 1].Base > pLdrInfoTable->mmTable[i].Base)
				{
					SMAP_ENTRY entry = pLdrInfoTable->mmTable[i];

					pLdrInfoTable->mmTable[i] = pLdrInfoTable->mmTable[i - 1];

					pLdrInfoTable->mmTable[i - 1] = entry;

					needToRecheck = true;
				}
			}
		}
	}

	// checking memory limit - do we have enough memory?
	PSMAP_ENTRY pMemEntry = &pLdrInfoTable->mmTable[pLdrInfoTable->mmEntriesCount - 1];

	if (pMemEntry->Base + pMemEntry->Length < 32 * 1024 * 1024)
	{

	}
}

void ProcessMultibootHeaderData(MULTIBOOT2_HEADER* pLdrInfoTable)
{
	BootHeaderError("Multiboot is not supported yet");
}

void XCoreLoaderProc()
{
	LOADER loaderId;
	LPVOID lpAddrOfBootHeader;

	// Let's get some significant information about loader that was used
	__asm mov lpAddrOfBootHeader, ebx;
	__asm mov loaderId, edx;

	// Initialize console functions
	init_console();

	// Simple greeting :)
	putline("Xelbera OS loader v0.1 (alpha)\n");

	// Let's detect CPU information
	detect_cpu();

	// Let's get som information about this PC - it MUST be given by loader
	switch (loaderId)
	{
		case LDR_XPRELDR:
			// XPreLdr.sys was used - let's get all the data from tables specified by loader
			ProcessXPreLdrData((PXPRELDRINFO)lpAddrOfBootHeader);
			break;
		case LDR_MULTIBOOT:
			// XCoreLdr.exe was loaded using MULTIBOOT loader (ex. GRUB).
			// let's parse all the data suplied by that loader
			ProcessMultibootHeaderData((MULTIBOOT2_HEADER*)lpAddrOfBootHeader);
			break;

		default:
			// We don't know this type of loader, so we can't get ready-to-use information
			// about this PC - it is not interesting for us to work with this loader.
			puts("XelberaOS could not be loaded using unknown boot loader!");
			halt();
			break;
	}

	
	// Creating default interrupt table
	IDT idt;
	
#ifdef DEBUG
	// if we are in debug mode - let's switch on debugger interface
	//InitGDB(&idt);
#endif

	// Activating interrupts
	idt.Activate();

	// no comments :)
	halt();


	// sys drive disk structure
	// $							- root directory
	//   system						- system directory
	//      JIT						- directory for platform-dependant JITs (ILx*.dll files are located here)
	//		Core					- directory for core (XCore.dll is here)
	//         Extensions			- directory for core-level extensions (drivers, all files [cext-s] from this 
	//								  directory will be loaded, but 
	//								  there are some limits for cexts to be loaded
	//									1) cext is managed-only module
	//									2) cext must be signed by a certificate
	//									3) unsigned cext-s runs in ring 1 (not ring 0) so there are some ristrictions
	//									4) if some unexpected problem occures cext is blocked forever
	//      Extensions				- directory for OS extensions (some plugins/codecs/etc)
	//		Sandbox					- directory for SandBox application (runs native code in userspace)
	//		Browser					- directory for Browser application (allows you to browse everything in one commomn way (files/web/photos...))

	// Test witch arch is better for OS - get list of IL assemblers by the priority

	// trying to load assembler (until first success)
	// LoadFileForExecution("/System/JIT/ILx86.dll" 0x00007e00)
	// -> this must fix all the references and alignments
	//    this will not do any calls to JIT due to type of file (native app)
	// if error occures - unload that DLL and load the next one

	// Loading core
	// LoadFileForExecution("/System/Code/XCore.dll", 0x00100000);
	// -> this must use memory mapped file and call something like IL2asm("/System/Code/XCore.dll", 0x00100000)
	//    due to type of file (managed app)

	// do simple setup of hardware (VESA or VGA driver, keyboard/mouse driver and etc.)

	// load cext drivers using IL2asm("/System/Code/XCore.dll") 
	// configure XCore to use modules that are loaded

	// enter the mode we have chosen (x86_64 long/x86 protected/???)

	// go to the core entry point (XCore.Main()) and pass all the information about the current memory map


	// This is an entry point for OS code loader.
	// at this point we are in x86 unreal mode (we are working with 1Mb+ memory)/
	// the next step for loading the kernel is a decidion if we are going to use x86 protected mode
	// or we are switching to x86-64.
	//
	// This dissision is made after testing some things, like does CPU has a support of X86-64,
	// will it make sence (ex. do you have more than 4Gb of memory) and so on.
	// The main idea of this is using the most apropreative way of using you PC to prevent
	// unnessesary overloads (like switching to x86 mode if we are not going to use advantages of x86-64 mode)

	// Let's load MSIL code of core
	LoadCoreCode();

	// Let's do some test of the environment
	if (IsX64Suitable())
	{
		// Ok, we are going to use x64 mode
		// Let's init environments
		PrepaireLongModeEnvironment();

		// We must precompile the code (JIT it briefly - just entrypoint should be ready for execution)
		PrecompileCoreForX64();

		// Let's execute the entry point of the core
		ExecuteX64Core();
	}
	else
	{
		// No, we are using x86 protected mode, not x86-64
		// Prepairing environment for entering protected mode
		PrepaireProtectedModeEnvironment();

		// We must precompile the code (JIT it briefly - just entrypoint should be ready for execution)
		PrecompileCoreForX86();

		// Let's execute the entry point of the core
		ExecuteX86Core();
	}

	// Do nothing forever!
	reboot();
}

bool IsX64Suitable()
{
	putline("Determinating PC features...");
	return false;
}
